package com.zhx.app.handler;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhx.core.common.LoginType;
import com.zhx.core.common.properties.SecurityProperties;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 自定义的认证成功类
 *
 * spring security 在认证成功的时候默认跳转到原本的请求的地方  现在需要自定义请求成功后返回用户信息
 *
 * 可以实现 AuthenticationSuccessHandler
 * 也可以继承 SavedRequestAwareAuthenticationSuccessHandler spring默认的成功处理器
 *
 * 完成后需要注入到config中去 并且配置自己的成功处理器
 *
 * @author zhx
 * @create 2017-11-02 11:24
 **/
@Component("myAuthentionSuccessHandler")
public class MyAuthentionSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    private  final static Logger logger = LoggerFactory.getLogger(MyAuthentionSuccessHandler.class);

    //spring 启动的时候自动注册的一个mapper 可以转换类
    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private SecurityProperties securityProperties;

    /**
     * 构建ClientDetails
     */
    @Autowired
    private ClientDetailsService clientDetailsService;


    @Autowired
    private AuthorizationServerTokenServices authorizationServerTokenServices;

    //Authentication 其中一个核心接口  基本上包含了所有需要的信息
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        logger.info("Oauth产生token开始!");

        /**
         * 第一步是解析请求头  解析出来当前请求的 clientid  使用 BasicAuthenticationFilter 中的代码即可
         */

        String header = request.getHeader("Authorization");
        if (header != null && header.startsWith("Basic ")) {
            try {
                String[] tokens = this.extractAndDecodeHeader(header, request);

                /**
                 * 取出请求头中的信息  切割为 clientId 和 clientSecret
                 * 使用 ClientDetailsService 和 clientId 就可以构造出来 ClientDetails
                 */

                assert tokens.length == 2;
                //clientId
                String clientId = tokens[0];
                //clientSecret
                String clientSecret = tokens[1];

                //使用 clientId 获取当前的 clientDetails 其实就是 获取配置文件中的 clientId 和 clientSecret
                ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);

                //比对一下 clientSecret 是否正确
                if(clientDetails == null){
                    throw new UnapprovedClientAuthenticationException("找到当前的请求Client信息,请确认请求头是否正确!"+clientId);
                }

                if(StringUtils.isBlank(clientDetails.getClientSecret()) || !clientDetails.getClientSecret().equals(clientSecret)){
                    throw new UnapprovedClientAuthenticationException("clientSecret不匹配!"+clientId);
                }

                //根据信息构建 TokenRequest
                TokenRequest custom = new TokenRequest(MapUtils.EMPTY_MAP, clientId, clientDetails.getScope(), "custom");
                //构建OAuth2Request
                OAuth2Request oAuth2Request =  custom.createOAuth2Request(clientDetails);

                /**
                 * 截止到此步  所有请求参数已经构建完毕
                 */

                OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
                /**
                 * 构建出来当前的accesstoken
                 */
                OAuth2AccessToken accessToken = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);

                httpServletResponse.setContentType("application/json,charset=UTF-8");
                httpServletResponse.getWriter().write(objectMapper.writeValueAsString(accessToken));

            }catch(Exception e){
                logger.error("切割请求头Authorization 出错:" + e.getMessage());
                throw e;
            }
        }else{
            throw new UnapprovedClientAuthenticationException("请求头中没找到Authorization 信息");
        }
    }

    /**
     * BasicAuthenticationFilter 中的切割的方法
     * @param header
     * @param request
     * @return
     * @throws IOException
     */
    private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException {
        byte[] base64Token = header.substring(6).getBytes("UTF-8");

        byte[] decoded;
        try {
            decoded = Base64.decode(base64Token);
        } catch (IllegalArgumentException var7) {
            throw new BadCredentialsException("Failed to decode basic authentication token");
        }

        String token = new String(decoded, "UTF-8");
        int delim = token.indexOf(":");
        if (delim == -1) {
            throw new BadCredentialsException("Invalid basic authentication token");
        } else {
            return new String[]{token.substring(0, delim), token.substring(delim + 1)};
        }
    }

}
